package biz.datalk.industrialland.common.encrypt;

import biz.datalk.industrialland.common.encrypt.exception.AESException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;

/**
 *
 * 使用JDK 1.8.0_161或更高版本时，不需要再安装JCE Policy File。JDK 1.8.0_161默认启用无限强度加密。
 * <a href="http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html">JCE8</a>
 * <a href="http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html">JCE7</a>
 * <a href="http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html">JCE6</a>
 *
 * <pre>
 * AES加密工具
 *
 * <a href="http://tool.chacuo.net/cryptaes">在线加密工具地址参考</a>
 *
 * AES加密模式：ECB/CBC/CTR/OFB/CFB
 * 填充：pkcs5padding/pkcs7padding/zeropadding/iso10126/ansix923
 * 数据块：128位/192位/256位
 * 密码：【设置加解密的密码，JAVA中有效密码为16位/24位/32位， 其中24位/32位需要JCE（Java 密码扩展无限制权限策略文件， 每个JDK版本对应一个JCE，百度即可找到）】
 * 偏移量：【iv偏移量，ECB不用设置】
 * 输出：base64/hex
 * 字符集：gb2312/gbk/gb18030/utf8
 * </pre>
 *
 * @author tarofang@163.com
 * @date 2019-08-25
 */
@SuppressWarnings({"DuplicatedCode", "JavaDoc"})
@Slf4j
public class AESTool {
    /** 可配置到Constant中，并读取配置文件注入 16位 */
    private static final String KEY = "datalk88datalk88";

    /** 参数分别代表 算法名称/加密模式/数据填充方式 */
    private static final String AES_ECB_MODEL = "AES/ECB/PKCS5Padding";
    private static final String AES_CBC_MODEL = "AES/CBC/PKCS5Padding";
    private static final String ENCODING = StandardCharsets.UTF_8.name();

    /** 加密算法 AES */
    public static final String ALGORITHM = "AES";
    private static final int INITIALIZE_LENGTH = 128;

    public static SecretKey getKey() {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
            keyGenerator.init(INITIALIZE_LENGTH);
            return keyGenerator.generateKey();
        } catch (Exception ex) {
            log.error("{}", ex.getMessage(), ex);
            throw new AESException(ex);
        }
    }

    public static String getkeyStr() {
        SecretKey key = getKey();
        byte[] encoded = key.getEncoded();
        return Base64.encodeBase64String(encoded);
    }

    /**
     * 加密
     *
     * @param content 加密的字符串
     * @param encryptKey key值
     *
     * @return 加密后的密文
     *
     * @throws AESException
     */
    public static String encrypt(String content, String encryptKey) throws AESException {
        return encrypt(content, encryptKey.getBytes());
    }

    public static String encrypt(String content, byte[] encryptKey) {
        try {
            Cipher cipher = Cipher.getInstance(AES_ECB_MODEL);
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey, ALGORITHM));
            byte[] b = cipher.doFinal(content.getBytes(ENCODING));
            // 采用base64算法进行转码,避免出现中文乱码
            return Base64.encodeBase64String(b);
        } catch (Exception ex) {
            log.error("{}", ex.getMessage(), ex);
            throw new AESException(ex);
        }
    }

    /**
     * 解密
     *
     * @param encryptStr 解密的字符串
     * @param decryptKey 解密的key值
     *
     * @return 解密后的明文
     *
     * @throws AESException
     */
    public static String decrypt(String encryptStr, String decryptKey) throws AESException {
        return decrypt(encryptStr, decryptKey.getBytes());
    }

    public static String decrypt(String encryptStr, byte[] decryptKey) {
        try {
            Cipher cipher = Cipher.getInstance(AES_ECB_MODEL);
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey, ALGORITHM));
            // 采用base64算法进行转码,避免出现中文乱码
            byte[] encryptBytes = Base64.decodeBase64(encryptStr);
            byte[] decryptBytes = cipher.doFinal(encryptBytes);
            return new String(decryptBytes);
        } catch (Exception ex) {
            log.error("{}", ex.getMessage(), ex);
            throw new AESException(ex);
        }
    }

    public static String decryptByte(byte[] encryptByte, String decryptKey) {
        return decryptByte(encryptByte, decryptKey.getBytes());
    }

    public static String decryptByte(byte[] encryptByte, byte[] decryptKey) {
        try {
            Cipher cipher = Cipher.getInstance(AES_ECB_MODEL);
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey, ALGORITHM));
            byte[] decryptBytes = cipher.doFinal(encryptByte);
            return new String(decryptBytes);
        } catch (Exception ex) {
            log.error("{}", ex.getMessage(), ex);
            throw new AESException(ex);
        }
    }

    public static byte[] encryptByte(String content, String encryptKey) {
        return encryptByte(content, encryptKey.getBytes());
    }

    public static byte[] encryptByte(String content, byte[] encryptKey) {
        try {
            Cipher cipher = Cipher.getInstance(AES_ECB_MODEL);
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey, ALGORITHM));
            return cipher.doFinal(content.getBytes(ENCODING));
        } catch (Exception ex) {
            log.error("{}", ex.getMessage(), ex);
            throw new AESException(ex);
        }
    }

    public static String encrypt(String content) throws AESException {
        return encrypt(content, KEY);
    }

    public static String decrypt(String encryptStr) throws AESException {
        return decrypt(encryptStr, KEY);
    }


    public static String encrypt(String plaintext, String secretKey, String iv) {
        return encrypt(plaintext, secretKey, iv, Boolean.FALSE);
    }

    /**
     * AES 加密
     *
     * @param plaintext 需要加密的明文
     * @param secretKey 加密key 长度 32 位
     * @param iv 加密向量 长度 16 位
     * @param urlSate base64 是否URL安全
     * @return 密文
     */
    public static String encrypt(String plaintext, String secretKey, String iv, boolean urlSate) {
        try {
            // secretKey 长度 32
            byte[] secretKeyBytes = secretKey.getBytes(ENCODING);
            SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyBytes, ALGORITHM);

            // iv 长度 16
            byte[] ivBytes = iv.getBytes(ENCODING);
            IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);

            byte[] content = plaintext.getBytes(ENCODING);

            Cipher cipher = Cipher.getInstance(AES_CBC_MODEL);
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
            byte[] cipherBytes = cipher.doFinal(content);
            // 采用base64算法进行转码,避免出现中文乱码
            return urlSate ? Base64.encodeBase64URLSafeString(cipherBytes) : Base64.encodeBase64String(cipherBytes);
        } catch (Exception ex) {
            log.error("{}", ex.getMessage(), ex);
            throw new AESException(ex);
        }
    }

    /**
     * AES 解密
     * @param ciphertext 加密字符串
     * @param secretKey 加密key 长度 32 位
     * @param iv 加密向量 长度 16 位
     * @return 明文
     */
    public static String decrypt(String ciphertext, String secretKey, String iv) {
        try {
            // secretKey 长度 32
            byte[] secretKeyBytes = secretKey.getBytes(ENCODING);
            SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyBytes, ALGORITHM);

            // iv 长度 16
            byte[] ivBytes = iv.getBytes(ENCODING);
            IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);

            byte[] encryptBytes = Base64.decodeBase64(ciphertext);

            Cipher cipher = Cipher.getInstance(AES_CBC_MODEL);
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
            byte[] decryptBytes = cipher.doFinal(encryptBytes);
            return new String(decryptBytes);
        } catch (Exception ex) {
            log.error("{}", ex.getMessage(), ex);
            throw new AESException(ex);
        }
    }

    public static byte[] encrypt(byte[] plaintextBytes, byte[] secretBytes, byte[] ivBytes) {
        try {
            SecretKeySpec secretKeySpec = new SecretKeySpec(secretBytes, ALGORITHM);
            IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
            Cipher cipher = Cipher.getInstance(AES_CBC_MODEL);
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
            return cipher.doFinal(plaintextBytes);
        } catch (Exception ex) {
            log.error("{}", ex.getMessage(), ex);
            throw new AESException(ex);
        }
    }

    public static byte[] decrypt(byte[] cipherBytes, byte[] secretBytes, byte[] ivBytes) {
        try {
            SecretKeySpec secretKeySpec = new SecretKeySpec(secretBytes, ALGORITHM);
            IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
            Cipher cipher = Cipher.getInstance(AES_CBC_MODEL);
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
            return cipher.doFinal(cipherBytes);
        } catch (Exception ex) {
            log.error("{}", ex.getMessage(), ex);
            throw new AESException(ex);
        }
    }


    public static void main(String[] args) {
        //版本1
//        String iv = "qOT5Jr3wVbwvF4Ms";
        //版本2
//        String iv = "Ik+5h2d98NIvkREu";
        String iv = "DmwAgOw7A+Fz1Du4";
        String sk = "M+g79TmqXWIsK8EB0uGfCQ==";
        String wenxiang = encrypt("wenxiang", sk, iv);

//        String de = decrypt("FaBdMuVcbYfAd3lPldLC4g", sk, iv);
        System.out.println(wenxiang);
//        System.out.println(de);

        String decrypt = decrypt("kpXxWHmvN-ib1I2z7NVJow=", sk);
        System.out.println(decrypt);




        // String sk = RandomStringUtils.randomAlphabetic(32);
//        String sk = getkeyStr();
//        String iv = RandomStringUtils.randomAlphabetic(16);
//        System.out.println("sk: " + sk);
//        System.out.println("iv: " + iv);
//
//        // String plaintext = "中文符号～！@#$^1234_-mmxxaa";
//        String plaintext = "aaammxxaa";
//        System.out.println("加密前：" + plaintext);
//        String encrypt = encrypt(plaintext, sk, iv, true);
//        System.out.println("加密后：" + encrypt);
//        // iv = "pNZAieJVDdFyirJk";
//        String decrypt = decrypt(encrypt, sk, iv);
//        System.out.println("解密后：" + decrypt);



        // System.out.println(getkeyStr());
        // Map<String, String> map = new HashMap<String, String>(4);
        // map.put("key", "value");
        // map.put("中文", "汉字");
        // String content = JsonUtil.getJsonFromObject(map);
        // System.out.println("加密前：" + content);
        //
        // String encrypt = encrypt(content, KEY);
        // System.out.println("加密后：" + encrypt);
        //
        // String decrypt = decrypt(encrypt, KEY);
        // System.out.println("解密后：" + decrypt);
    }
}